import {effect} from ".";
import {track, trigger} from "./effect";
type TypeOf<T, K extends keyof T> = T extends any ? T[K] : never;
type computedHandler = {
  get: () => any;
  set?: (arg: any) => any;
};
type computedOpt = computedHandler | (() => any);
export function defineComputed(opt: computedOpt) {
  let getter, setter;
  const defSetter = () => console.error("computed cannot use setter");
  if (opt instanceof Function) {
    getter = opt;
    setter = defSetter;
  } else {
    getter = opt.get;
    setter = opt.set || defSetter;
  }

  return new ComputedImpl(getter, setter);
}

class ComputedImpl {
  private _get;
  private _set;
  private _effect;
  private _isDirty:boolean;
  private _value:any;
  constructor(
    getter: TypeOf<computedHandler, "get">,
    setter: TypeOf<Required<computedHandler>, "set">,
  ) {
    this._isDirty = true;
    this._get = getter;
    this._set = setter;
    this._effect = effect(getter,{
      lazy:true,
      scheduler:() =>{
        if(!this._isDirty){
          this._isDirty = true;
          trigger(this,"value");
        }
      }
    });
  }

  get value() {
    track(this, "value");
    if(this._isDirty){
      this._isDirty = false;
      this._value = this._effect();
    }
    return this._value;
  }

  set value(val) {
    this._set(val);
    trigger(this, "value");
  }
}
